import { requestSDspotMarketZcq } from "@src/api/fetch";
import { getSpotMartetZcqReqInfo } from "@src/entries/options/generate";
import CryptoJS from "crypto-js";
import { ChunkUploader, FormDataWithBlobs } from "@src/utils/ChunkUploader";
import { base64ToBlob, generateUniqueTimestamp } from "@src/utils/common";
import { pyApiBaseUrl } from "@src/config/accountInfo";

// 创建 FormDataWithBlobs 类型的示例数据
type MyFormData = FormDataWithBlobs<'slidingImage' | 'backImage', 'slidingImage', 'backImage'>;

// 初始化上传器
const uploader = new ChunkUploader({
  uploadAd: `${pyApiBaseUrl}/uploadChunks`,
  chunkSize: 50 * 1024, // 每片最大50kb
  maxRetries: 3, // 每片最多重试3次
});

export class ImageVerification {
  UUIDs: {
    slider:string
    point:string
  }
  captchaType = "blockPuzzle"
  headers:HeadersInit | undefined
  private initPromise: Promise<void>

  fileId:string = ''           // 上传的图片验证组id
  moveLeftDistance:number = 0  // python 图像算法解析的结果
  // 获取交易中心验证图片组接口返参信息
  getVerifyImagesResInfo: {
    secretKey:string,
    backToken:string,
    slidingImage:string,
    backImage:string
  } = {
    secretKey:'',
    backToken:'',
    slidingImage:'',
    backImage:''
  }
  maxRetries = 3 // 最大重试次数，默认3次
  curRetries = 0 // 当前已尝试的次数

  constructor() {
    this.UUIDs = this.initializeUUIDs()
    this.initPromise = this.initReqInfo()
  }


  /**
   * 加密数据函数
   * @param word 要加密的内容
   * @param keyWord 服务器随机返回的关键字
   * @returns 加密后的字符串
   */
  aesEncrypt = (word: string, keyWord: string): string => {
    const key = CryptoJS.enc.Utf8.parse(keyWord);
    const srcs = CryptoJS.enc.Utf8.parse(word);

    const encrypted = CryptoJS.AES.encrypt(srcs, key, {
      mode: CryptoJS.mode.ECB,
      padding: CryptoJS.pad.Pkcs7,
    });

    return encrypted.toString();
  }

  /**
   * 解密数据函数
   * @param ciphertext 加密后的内容
   * @param keyWord 服务器随机返回的关键字
   * @returns 解密后的字符串
   */
  aesDecrypt = (ciphertext: string, keyWord: string): string => {
    const key = CryptoJS.enc.Utf8.parse(keyWord);

    const decrypted = CryptoJS.AES.decrypt(ciphertext, key, {
      mode: CryptoJS.mode.ECB,
      padding: CryptoJS.pad.Pkcs7,
    });

    return decrypted.toString(CryptoJS.enc.Utf8);
  }

  /** 生成 获取验证图片组的ID */
  initializeUUIDs = () => {
    const hexDigits = "0123456789abcdef";
    const s = Array(36).fill("");
  
    for (let i = 0; i < 36; i++) {
      if (i === 8 || i === 13 || i === 18 || i === 23) {
        s[i] = "-"; // 插入分隔符
      } else {
        s[i] = hexDigits[Math.floor(Math.random() * 16)];
      }
    }
  
    s[14] = "4"; // 确保第 14 位为 '4' (UUID v4)
    s[19] = hexDigits[(parseInt(s[19], 16) & 0x3) | 0x8]; // 确保第 19 位符合变体规则
  
    const uuidString = s.join("");

    return {
      slider:`slider-${uuidString}`,
      point:`point-${uuidString}`
    }

  }

  /** 初始化请求的基本数据 */
  initReqInfo = async () => {
    const {headers} = await getSpotMartetZcqReqInfo()
    this.headers = headers
  }

  /** 初始化交易中心验证图片组接口返参信息*/
  initVerifyImagesResInfo = async () => {
    this.getVerifyImagesResInfo = {
      secretKey:'',
      backToken:'',
      slidingImage:'',
      backImage:''
    }
  }

  /** 图像算法破解成功后，对坐标数据继续加密 */
  pointEncrypt = ({secretKey, moveLeftDistance}:{secretKey?:string, moveLeftDistance?:number} = {}) => {
    secretKey = secretKey || this.getVerifyImagesResInfo.secretKey
    moveLeftDistance = moveLeftDistance || this.moveLeftDistance

    const pointJson = secretKey ? this.aesEncrypt(JSON.stringify({x:moveLeftDistance,y:5.0}),secretKey):JSON.stringify({x:moveLeftDistance,y:5.0})
    return pointJson
  }

  /** 验证成功后，根据验证图片组的信息和滑动距离生成验证结果code */
  afterCheckGetCaptchaVerification = ({secretKey, backToken, moveLeftDistance}:{secretKey?:string, backToken?:string, moveLeftDistance?:number} = {}) => {
    secretKey = secretKey || this.getVerifyImagesResInfo.secretKey
    backToken = backToken || this.getVerifyImagesResInfo.backToken
    moveLeftDistance = moveLeftDistance || this.moveLeftDistance
    if(!(secretKey && backToken && moveLeftDistance)) {
      console.error('afterCheckGetCaptchaVerification 缺失必要的参数！')
      return
    }
    const captchaVerification = secretKey ? this.aesEncrypt(backToken+'---'+JSON.stringify({x:moveLeftDistance,y:5.0}),secretKey):backToken+'---'+JSON.stringify({x:moveLeftDistance,y:5.0})
    return captchaVerification
  }

  /** 获取交易中心的验证图片组 */
  getVerifyImages = async () => {
    try {
      const params = {
        captchaType: this.captchaType,
        clientUid: this.UUIDs.slider,
        ts: Date.now()
    }
      const res = await requestSDspotMarketZcq(
        `https://pmos.sd.sgcc.com.cn:18080/zcq/captcha/get.do`,
        JSON.stringify(params),
        { method:'POST', headers:{...this.headers, 'Content-Type':'application/json;charset=UTF-8'}}
      )
      if(res.repCode !=="0000") return Promise.reject(new Error('获取交易中心的验证图片组失败'))
      
      this.getVerifyImagesResInfo = {
        secretKey: res.repData.secretKey,
        backToken: res.repData.token,
        slidingImage: res.repData.jigsawImageBase64,
        backImage: res.repData.originalImageBase64,
      }

      return Promise.resolve(this.getVerifyImagesResInfo)
    } catch(e) {
      console.error(e)
      return Promise.reject(e)
    }
  }

  /** 分片上传验证图片组 */
  uploadChunks = async (images:{slidingImage?:string,backImage?:string} = {})=> {
    try {
      const {slidingImage, backImage} = this.getVerifyImagesResInfo
      const myData: MyFormData = {
        slidingImage: base64ToBlob(images.slidingImage || slidingImage),
        backImage: base64ToBlob(images.backImage || backImage),
      }

      const fileId = generateUniqueTimestamp()

      // 开始上传
      const res = await uploader.upload(myData, fileId)
      this.fileId = fileId

      console.log('uploadChunks', res)
      return Promise.resolve({fileId})
    } catch(e) {
      console.error(e)
      return Promise.reject(e)
    }
  }

  /** 摘挂牌paython图像验证算法分析*/
  zgpCaptchaSolver = async (filesInfo:{fileId?:string} = {})=> {
    try {
      const fileId = filesInfo?.fileId || this.fileId

      const res = await requestSDspotMarketZcq(
        `${pyApiBaseUrl}/zgpCaptchaSolver`,
        JSON.stringify({fileId}),
        { method:'POST', headers:{'Content-Type':'application/json'} }
      )
      console.log('zgpCaptchaSolver', res)
      this.moveLeftDistance = res?.target?.[0] || 0
      if(!this.moveLeftDistance) return Promise.reject(new Error('zgpCaptchaSolver 验证失败'))
      this.moveLeftDistance += 0.12663513183595
      return Promise.resolve({moveLeftDistance: this.moveLeftDistance, fileId})
    } catch(e) {
      console.error(e)
      return Promise.reject(e)
    }
  }

  /** 获取交易中心验证结果 */
  checkCode = async () => {
    try {
      const params = {
        captchaType: this.captchaType,
        pointJson: this.pointEncrypt(),
        token: this.getVerifyImagesResInfo.backToken,
        clientUid: this.UUIDs.slider,
        ts: Date.now()
      }
      const res = await requestSDspotMarketZcq(
        'https://pmos.sd.sgcc.com.cn:18080/zcq/captcha/check.do',
        JSON.stringify(params),
        { method:'POST', headers:{... this.headers, 'Content-Type':'application/json;charset=UTF-8'} }
      )
      
      if(res?.repCode !=="0000") {
        console.error('算法分析结果交易中心验证失败')
      }
      return Promise.resolve(res)
    }catch(e) {
      console.error(e)
      return Promise.reject(e)
    }
  }

  /** 所有验证步骤 合成一个函数*/
  allVerifyProcess = async () => {
    try{
      await this.getVerifyImages()
      await this.uploadChunks()
      await this.zgpCaptchaSolver()

      const finalCheckRes = await this.checkCode()
      return Promise.resolve(finalCheckRes)
    }catch(e) {
      console.error(e)
      this.moveLeftDistance = 0
      this.initVerifyImagesResInfo()
      return Promise.resolve({})
    }
  }

  /**
   * 获取加密的图片滑块验证的各步骤和最终结果
  */
  getFinalCheckResult = async (first = true):Promise<string | undefined> => {
    let captchaVerification:string | undefined = undefined
    try {
      await this.initPromise
      ++this.curRetries
      const finalCheckRes = await this.allVerifyProcess()

      captchaVerification = this.afterCheckGetCaptchaVerification()

      console.log(`第 ${this.curRetries} 次验证：${captchaVerification}`)
      if((finalCheckRes && finalCheckRes?.repCode) !=="0000" || !captchaVerification) {
        captchaVerification = undefined
        
        if(this.curRetries < this.maxRetries) {
          captchaVerification = await this.getFinalCheckResult(false)
        }
      }

      return Promise.resolve(captchaVerification)
    }catch(e) {
      console.error(e)
      return Promise.resolve(captchaVerification)
    }finally {
      if(first) this.curRetries = 0
    }
  }

}